# -*- coding: utf-8 -*-

##--------------------------------------#
## Kvasir
##
## (c) 2010-2014 Cisco Systems, Inc.
## (c) 2015 Kurt Grutzmacher
##
## Exploits controller
##
## Author: Kurt Grutzmacher <grutz@jingojango.net>
##--------------------------------------#

from skaldship.hosts import host_title_maker, get_host_record, create_hostfilter_query
from NexposeAPI import NexposeAPI
import logging
logger = logging.getLogger("web2py.app.kvasir")
crud.settings.formstyle = formstyle_bootstrap_kvasir

@auth.requires_login()
def index():
    return dict()

##-------------------------------------------------------------------------
## exploits
##-------------------------------------------------------------------------

@auth.requires_login()
def add():
    if request.extension in ['load', 'json']:
        form=SQLFORM(db.t_exploits, buttons=[], _action=URL('add', extension=request.extension), _id="exploit_add_form")
    else:
        form=SQLFORM(db.t_exploits, _action=URL('add', extension=request.extension), _id="exploit_add_form")
    if form.accepts(request.vars, session):
        response.flash = 'Exploit added'
        response.headers['web2py-component-command'] = "exploitstable.fnReloadAjax();"
        return ""
    elif form.errors:
        response.flash = "Error in form submission"
        return TABLE(*[TR(k, v) for k, v in form.errors.items()])

    response.title = "%s :: Add Exploit" % (settings.title)
    return dict(form=form)

@auth.requires_login()
def detail():
    record = db.t_exploits(request.args(0)) or redirect(URL('default', 'error', vars={'msg': T('Exploit record not found')}))
    form=crud.read(db.t_exploits,record)
    response.title = "%s :: Exploit Detail" % (settings.title)
    return dict(form=form)

@auth.requires_login()
def edit():
    record = db.t_exploits(request.args(0)) or redirect(URL('default', 'error', vars={'msg': T('Exploit record not found')}))
    form=crud.update(db.t_exploits,record,next='detail/[id]',
                     ondelete=lambda form: redirect(URL('list')),
                     onaccept=crud.archive)
    response.title = "%s :: Update Exploit" % (settings.title)
    return dict(form=form)

@auth.requires_signature()
@auth.requires_login()
def delete():
    count = 0
    for r in request.vars.ids.split('|'):
        if r is not None:
            db(db.t_exploits.id == r).delete()
            count += 1
    db.commit()
    response.flash = "%s Exploit(s) deleted" % (count)
    response.headers['web2py-component-command'] = "exploitstable.fnReloadAjax(); jQuery('.datatable tr.DTTT_selected').removeClass('DTTT_selected');"

@auth.requires_login()
def list():
    aaData = []
    record = None
    if request.extension == "json":

        rows = db(db.t_exploits.id > 0).select()

        for r in rows:
            aTxt = {}
            aaData.append({
                '0': A('edit', _target="exploits_%s" % (r.id), _href=URL('edit.html', args=r.id)).xml(),
                '1': r.f_name,
                '2': r.f_title,
                '3': r.f_description,
                '4': r.f_source,
                '5': r.f_rank,
                '6': r.f_level,
                '7': r.f_vulnid,
                '8': r.f_cve,
                'DT_RowId': r.id
            })

        result = { 'sEcho': request.vars.sEcho,
                   'iTotalRecords': len(aaData),
                   'aaData': aaData,
                   }

        return result

    response.title = "%s :: Exploits" % (settings.title)
    return dict()

@auth.requires_login()
def by_vulnid():
    """
    Returns a list of exploits for a vulnerability id
    """
    rows = db(db.t_exploits.f_vulnid.contains(request.args(0))).select()
    response.title = "%s :: Exploits by Vuln ID" % (settings.title)
    return rows

##-------------------------------------------------------------------------
## exploit list support (upload xml, match)
##-------------------------------------------------------------------------

@auth.requires_login()
def connect_exploits():
    """
    Call the connect_exploits() function which links known vulnerabilities to
    exploits based on f_vulnid or f_cve
    """
    form = SQLFORM.factory(
        Field('f_taskit', type='boolean', default=auth.user.f_scheduler_tasks, label=T('Run in background task')),
    )

    from skaldship.exploits import connect_exploits
    if form.accepts(request.vars, session):
        if form.vars.f_taskit:
            task = scheduler.queue_task(
                connect_exploits,
                group_name=settings.scheduler_group_name,
                sync_output=5,
                timeout=settings.scheduler_timeout,
            )
            if task.id:
                redirect(URL('tasks', 'status', args=task.id))
            else:
                response.flash = "Error submitting job: %s" % (task.errors)
        else:
            connect_exploits()
            response.flash = "Exploits and vulnerabilities connected"
            redirect(URL('list'))

    response.title = "%s :: Connect Exploits" % (settings.title)
    return dict(form=form)

@auth.requires_login()
def import_canvas_xml():
    """
    Process ImmunitySec's Exploit.xml which can be genrated from the URL
    http://exploitlist.immunityinc.com/ or by running ./canvasengine.py -e
    from your CANVAS directory

    http://exploitlist.immunityinc.com/home/serve/live
    """
    import os
    kvasir_path = os.path.join(request.folder, 'static/etc')
    form = SQLFORM.factory(
        Field('f_filename', 'upload', uploadfolder=os.path.join(request.folder, 'data/misc'), label=T('XML File')),
        Field('f_use_kvasir_local', 'boolean', label=T('Use Kvasir static path')),
        Field('f_use_local', 'boolean', label=T('Use local file path')),
        Field('f_pathname', 'string', default=kvasir_path, label=T('Local path')),
        Field('f_download', 'boolean', label=T('Download')),
        Field('f_taskit', type='boolean', default=auth.user.f_scheduler_tasks, label=T('Run in background task')),
        col3 = {
            'f_use_kvasir_local': 'static/etc/canvas_exploits.xml',
            'f_use_local': 'Directory where canvas_exploits.xml is located',
            'f_download': 'Download from ImmunitySec website',
        }
    )

    if form.errors:
        response.flash = 'Error in form'
    elif form.accepts(request.vars, session):
        if form.vars.f_use_local:
            filename = os.path.join(form.vars.f_pathname, 'canvas_exploits.xml')
        elif form.vars.f_use_kvasir_local:
            filename = os.path.join(request.folder,'static','etc','canvas_exploits.xml')
        elif form.vars.f_download:
            filename = None
        else:
            filename = os.path.join(request.folder,'data','misc',form.vars.f_filename)

        if form.vars.f_taskit:
            task = scheduler.queue_task(
                canvas_exploit_xml,
                pargs=[filename],
                group_name=settings.scheduler_group_name,
                sync_output=5,
                timeout=settings.scheduler_timeout,
            )
            if task.id:
                redirect(URL('tasks', 'status', args=task.id))
            else:
                response.flash = "Error submitting job: %s" % (task.errors)
        else:
            from skaldship.canvas import process_exploits
            from skaldship.exploits import connect_exploits
            process_exploits(filename)
            connect_exploits()
            response.flash = "Canvas Exploit data uploaded"
            redirect(URL('list'))

    response.title = "%s :: Import ImmunitySec CANVAS Exploits XML" % (settings.title)
    return dict(form=form)

@auth.requires_login()
def import_nexpose_xml():
    """
    Insert/Update exploit references from Nexpose exploits.xml file

    File is located in /opt/rapid7/nexpose/plugins/conf
    """
    import os
    response.title = "%s :: Import Nexpose Exploits XML" % (settings.title)
    form = SQLFORM.factory(
        Field('f_filename', 'upload', uploadfolder=os.path.join(request.folder, 'data', 'misc'), label=T('XML File')),
        Field('f_use_kvasir_local', 'boolean', label=T('Use Kvasir static path')),
        Field('f_use_local', 'boolean', label=T('Use local file path')),
        Field('f_pathname', 'string', default="/opt/rapid7/nexpose/plugins/conf", label=T('Local pathname')),
        Field('f_taskit', type='boolean', default=auth.user.f_scheduler_tasks, label=T('Run in background task')),
        col3 = {
            'f_use_kvasir_local': 'static/etc/nexpose_exploits.xml',
            'f_use_local': 'Directory where exploits.xml is located',
            'f_pathname': 'Requires Nexpose and possibly root access'
        }
    )

    if form.errors:
        response.flash = 'Error in form'
    elif form.accepts(request.vars, session):
        # process nexpose exploits.xml file

        if form.vars.f_use_local:
            filename = os.path.join(form.vars.f_pathname, 'exploits.xml')
        elif form.vars.f_use_kvasir_local:
            filename = os.path.join(request.folder,'static','etc','nexpose_exploits.xml')
        else:
            filename = os.path.join(request.folder,'data', 'misc', form.vars.f_filename)

        if form.vars.f_taskit:
            task = scheduler.queue_task(
                nexpose_exploit_xml,
                pargs=[filename],
                group_name=settings.scheduler_group_name,
                sync_output=5,
                timeout=settings.scheduler_timeout,
            )
            if task.id:
                redirect(URL('tasks', 'status', args=task.id))
            else:
                response.flash = "Error submitting job: %s" % (task.errors)
        else:
            from skaldship.nexpose import process_exploits
            from skaldship.exploits import connect_exploits
            process_exploits(filename)
            connect_exploits()
            redirect(URL('list'))

    return dict(form=form)
